package com.dianrong.common.uniauth.client.custom;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.access.regular.SSRegularPattern;
import org.springframework.util.Assert;

import com.dianrong.common.uniauth.client.custom.model.AllDomainUserExtInfo;
import com.dianrong.common.uniauth.client.custom.model.SingleDomainUserExtInfo;
import com.dianrong.common.uniauth.client.custom.model.UserExtInfoParam;
import com.dianrong.common.uniauth.common.bean.dto.DomainDto;
import com.dianrong.common.uniauth.common.bean.dto.PermissionDto;
import com.dianrong.common.uniauth.common.bean.dto.UserDto;
import com.dianrong.common.uniauth.common.client.DomainDefine;

/**
 * uniauth对外的UserDetails实现
 * 
 * @author wanglin
 */
public class UserExtInfo implements UserDetails {
    private static final long serialVersionUID = 8347558918889027136L;
    // 通过账号密码登陆的域所对应的userExtInfo,可以通过该对象知道具体是从哪一个域登陆的
    private SingleDomainUserExtInfo loginDomainUserExtInfo;

    // 所有域共享的用户信息
    private AllDomainUserExtInfo allDomainUserExtInfo = new AllDomainUserExtInfo();

    /**
     * get the correct UserExtInfo
     * 
     * @return not null
     */
    private SingleDomainUserExtInfo getCurrentDomainUserExtInfo() {
        SingleDomainUserExtInfo currentDomainUserExtInfo = this.allDomainUserExtInfo.getUserDetail(DomainDefine.getStaticDomainCode());
        // 用户没有对应域的权限 需要构造一个空权限的对象
        if (currentDomainUserExtInfo == null) {
            SingleDomainUserExtInfo emptyUserInfo = SingleDomainUserExtInfo.emptyAuthorityUserInfo(this.loginDomainUserExtInfo.getUsername(), this.loginDomainUserExtInfo.getId(),
                    this.loginDomainUserExtInfo.getUserDto(), new DomainDto().setCode(DomainDefine.getStaticDomainCode()));
            // cache
            SingleDomainUserExtInfo exsitOne = this.allDomainUserExtInfo.addUserDetailIfAbsent(DomainDefine.getStaticDomainCode(), emptyUserInfo);
            if (exsitOne == null) {
                currentDomainUserExtInfo = emptyUserInfo;
            } else {
                currentDomainUserExtInfo = exsitOne;
            }
        }
        return currentDomainUserExtInfo;
    }

    public Boolean hasDomain(String domainPerm) {
        return getCurrentDomainUserExtInfo().hasDomain(domainPerm);
    }

    public Boolean hasPrivilege(String privilegePerm) {
        return getCurrentDomainUserExtInfo().hasPrivilege(privilegePerm);
    }

    public Boolean hasAnyPrivilege(String... privilegePerms) {
        return getCurrentDomainUserExtInfo().hasAnyPrivilege(privilegePerms);
    }

    public Boolean hasAllPrivileges(String... privilegePerms) {
        return getCurrentDomainUserExtInfo().hasAllPrivileges(privilegePerms);
    }

    /**
     * get current user's all permitted regular patterns set
     * 
     * @return unmodifiable set , not null
     */
    public Set<SSRegularPattern> getAllPermittedRegularPattern() {
        return getCurrentDomainUserExtInfo().getAllPermittedRegularPattern();
    }

    public UserExtInfo(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked,
            Collection<? extends GrantedAuthority> authorities, Long id, UserDto userDto, DomainDto domainDto, Map<String, Set<String>> permMap) {
        this(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id, userDto, domainDto, permMap, null);
    }

    public UserExtInfo(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked,
            Collection<? extends GrantedAuthority> authorities, Long id, UserDto userDto, DomainDto domainDto, Map<String, Set<String>> permMap,
            Map<String, Set<PermissionDto>> permDtoMap) {
        this(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id, userDto, domainDto, permMap, permDtoMap,
                new HashMap<String, UserExtInfoParam>());
    }

    public UserExtInfo(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked,
            Collection<? extends GrantedAuthority> authorities, Long id, UserDto userDto, DomainDto domainDto, Map<String, Set<String>> permMap,
            Map<String, Set<PermissionDto>> permDtoMap, Map<String, UserExtInfoParam> userExtInfos) {
        this.loginDomainUserExtInfo = new SingleDomainUserExtInfo(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities, id, userDto,
                domainDto, permMap, permDtoMap);
        Assert.notNull(userExtInfos);
        for (String domainCode : userExtInfos.keySet()) {
            UserExtInfoParam userExtInfo = userExtInfos.get(domainCode);
            this.allDomainUserExtInfo.addUserDetail(domainCode,
                    new SingleDomainUserExtInfo(userExtInfo.getUsername(), userExtInfo.getPassword(), userExtInfo.isEnabled(), userExtInfo.isAccountNonExpired(),
                            userExtInfo.isCredentialsNonExpired(), userExtInfo.isAccountNonLocked(), userExtInfo.getAuthorities(), userExtInfo.getId(), userExtInfo.getUserDto(),
                            userExtInfo.getDomainDto(), userExtInfo.getPermMap(), userExtInfo.getPermDtoMap()));
        }
        this.allDomainUserExtInfo.addUserDetailIfAbsent(domainDto.getCode(), this.loginDomainUserExtInfo);
    }

    /**
     * @param currentLoginDomainUserInfo can not be null
     * @param useAllDomainUserInfoShareMode
     * @param userExtInfos
     */
    public static UserExtInfo build(UserExtInfoParam currentLoginDomainUserInfo, Map<String, UserExtInfoParam> userExtInfos) {
        Assert.notNull(currentLoginDomainUserInfo);
        return new UserExtInfo(currentLoginDomainUserInfo.getUsername(), currentLoginDomainUserInfo.getPassword(), currentLoginDomainUserInfo.isEnabled(),
                currentLoginDomainUserInfo.isAccountNonExpired(), currentLoginDomainUserInfo.isCredentialsNonExpired(), currentLoginDomainUserInfo.isAccountNonLocked(),
                currentLoginDomainUserInfo.getAuthorities(), currentLoginDomainUserInfo.getId(), currentLoginDomainUserInfo.getUserDto(), currentLoginDomainUserInfo.getDomainDto(),
                currentLoginDomainUserInfo.getPermMap(), currentLoginDomainUserInfo.getPermDtoMap(), userExtInfos);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return getCurrentDomainUserExtInfo().getAuthorities();
    }

    @Override
    public String getPassword() {
        return getCurrentDomainUserExtInfo().getPassword();
    }

    @Override
    public String getUsername() {
        return getCurrentDomainUserExtInfo().getUsername();
    }

    @Override
    public boolean isAccountNonExpired() {
        return getCurrentDomainUserExtInfo().isAccountNonExpired();
    }

    @Override
    public boolean isAccountNonLocked() {
        return getCurrentDomainUserExtInfo().isAccountNonLocked();
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return getCurrentDomainUserExtInfo().isCredentialsNonExpired();
    }

    @Override
    public boolean isEnabled() {
        return getCurrentDomainUserExtInfo().isEnabled();
    }

    // 为了兼容以前的版本
    public Long getId() {
        return getCurrentDomainUserExtInfo().getId();
    }

    public UserExtInfo setId(Long id) {
        getCurrentDomainUserExtInfo().setId(id);
        return this;
    }

    public UserDto getUserDto() {
        return getCurrentDomainUserExtInfo().getUserDto();
    }

    public UserExtInfo setUserDto(UserDto userDto) {
        getCurrentDomainUserExtInfo().setUserDto(userDto);
        return this;
    }

    public DomainDto getDomainDto() {
        return getCurrentDomainUserExtInfo().getDomainDto();
    }

    public UserExtInfo setDomainDto(DomainDto domainDto) {
        getCurrentDomainUserExtInfo().setDomainDto(domainDto);
        return this;
    }

    public Map<String, Set<String>> getPermMap() {
        return getCurrentDomainUserExtInfo().getPermMap();
    }

    public UserExtInfo setPermMap(Map<String, Set<String>> permMap) {
        getCurrentDomainUserExtInfo().setPermMap(permMap);
        return this;
    }

    public Map<String, Set<PermissionDto>> getPermDtoMap() {
        return getCurrentDomainUserExtInfo().getPermDtoMap();
    }

    public UserExtInfo setPermDtoMap(Map<String, Set<PermissionDto>> permDtoMap) {
        getCurrentDomainUserExtInfo().setPermDtoMap(permDtoMap);
        return this;
    }

    public final SingleDomainUserExtInfo getLoginDomainUserExtInfo() {
        return loginDomainUserExtInfo;
    }

    public final AllDomainUserExtInfo getAllDomainUserExtInfo() {
        return allDomainUserExtInfo;
    }

    public final UserExtInfo setLoginDomainUserExtInfo(SingleDomainUserExtInfo loginDomainUserExtInfo) {
        this.loginDomainUserExtInfo = loginDomainUserExtInfo;
        return this;
    }

    public final UserExtInfo setAllDomainUserExtInfo(AllDomainUserExtInfo allDomainUserExtInfo) {
        this.allDomainUserExtInfo = allDomainUserExtInfo;
        return this;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof UserExtInfo) {
            UserExtInfo userInfo = (UserExtInfo) obj;
            // tenancyId and email must be equal
            return this.loginDomainUserExtInfo.equals(userInfo.getLoginDomainUserExtInfo());
        }
        return false;
    }

    @Override
    public int hashCode() {
        return this.loginDomainUserExtInfo.hashCode();
    }
}
